﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TextEditor4.Parser;

namespace TextEditor4.Refactoring
{
    class Refactoring
    {
        private IParseNodeType parseTree;
        private int varNum;
        private bool findWantedVar = false;
        private bool findDefVar = false;

        public bool FindDefVar
        {
            get { return findDefVar; }
            set { findDefVar = value; }
        }

        private string varName = "";
        private string newVar = "Foo";
        private double sectionB;
        private double sectionE;
        public List<int> refPositions = new List<int>();
        private bool tmp = true;
        public Refactoring(IParseNodeType pT, int varNum)
        {
            this.parseTree = pT;
            this.varNum = varNum;
        }
        private bool refFunName = false;
        public IParseNodeType Refactor(IParseNodeType currentNode)
        {

            ///спустились до заявленного места
            while (!findWantedVar)
            {
                if (currentNode.listOfChildren.Count != 0)
                {
                    Refactor(currentNode.listOfChildren[getNumOfOptimalChild(currentNode)]);
                }
                else
                {
                    varName = currentNode.usedVariables[0];
                    findWantedVar = true;
                }
            }
            ///поднялись до обявления
            while (!findDefVar)
            {
                for (int i = currentNode.listOfChildren.Count - 1; i >= 0; i--)
                {
                    ///если место объявления лежит в том же узле =) Нихрена не понятно, верно?
                    ///нам не нужно смотерть
                         try
                    {
                        ///int i; 
                        if ((currentNode.listOfChildren[i].name.Equals("LexLib.LexemNameSpace.TypeDefinition")) && (currentNode.listOfChildren[i].usedVariables[0].Equals(varName)))
                        {
                            
                            findDefVar = true;
                            currentNode.listOfChildren[i].usedVariables[0] = newVar;
                            refPositions.Add(currentNode.listOfChildren[i].listOfChildren[0].begIndex);
                            i = 0;

                            try
                            {
                                if (currentNode.listOfChildren[i + 1].name.Equals("Arguments for function"))
                                {
                                    refFunName = true;

                                }
                            }
                            catch (Exception e) { }
                        }
                        else if (((currentNode.listOfChildren[i].name.Equals("Arguments for function")) || (currentNode.listOfChildren[i].name.Equals("Condition for cycle 'For'"))) && (isTypeDefIn(currentNode.listOfChildren[i])))
                        {
                            findDefVar = true;
                            i = 0;
                        }
                        else if ((currentNode.listOfChildren[i].name.Equals("LexLib.LexemNameSpace.Function")))
                        {
                            if ((currentNode.listOfChildren[i].listOfChildren[0].name.Equals("LexLib.LexemNameSpace.TypeDefinition")) && (currentNode.listOfChildren[i].listOfChildren[0].usedVariables[0].Equals(varName)))
                            {
                                findDefVar = true;
                                currentNode.listOfChildren[i].listOfChildren[0].usedVariables[0] = newVar;
                                refPositions.Add(currentNode.listOfChildren[i].listOfChildren[0].listOfChildren[0].begIndex);
                                i = 0;
                                
                            }
                        }

                    }
                    catch (Exception e)
                    { }

                }
                if ((tmp == true) && (findDefVar == true)&&(!refFunName))
                {
                    sectionB = currentNode.begIndex;
                    sectionE = currentNode.endIndex;
                    tmp = false;
                    foreach (IParseNodeType node in currentNode.listOfChildren)
                    {

                        if ((node.name == "LexLib.LexemNameSpace.VarNameType") && (node.usedVariables[0] == varName) &&
                            (node.begIndex >= sectionB) && (node.endIndex <= sectionE))
                        {
                            node.usedVariables[0] = newVar;
                            refPositions.Add(node.begIndex);
                        }
                        try
                        {
                            if ((node.name.Equals("LexLib.LexemNameSpace.TypeDefinition")) &&
                                (node.listOfChildren[0].name.Equals(varName))&&
                                (node.begIndex >= sectionB) && (node.endIndex <= sectionE))
                            {
                                node.listOfChildren[0].usedVariables[0] = newVar;
                                refPositions.Add(node.listOfChildren[0].begIndex);
                            }
                            if ((node.begIndex >= sectionB) && (node.endIndex <= sectionE) &&
                               (node.name.Equals("LexLib.LexemNameSpace.TypeDefinition")) &&
                                (node.listOfChildren[0].Equals("LexLib.LexemNameSpace.Modificator")) &&
                                (node.listOfChildren[0].listOfChildren[0].usedVariables[0].Equals(varName)))
                            {
                                node.listOfChildren[0].listOfChildren[0].usedVariables[0] = newVar;
                                refPositions.Add(node.listOfChildren[0].listOfChildren[0].begIndex);
                            }
                        
                        }
                        catch (Exception ex)
                        { }
                         if ((node.usedVariables.Contains(varName))/*&&(!definitionInChild(varName,node))*/)
                        {
                            Refactor(node);
                        }
                    }
                }
                return null;
            }
            ///сюда должны зайти один раз
            if (tmp == true)
            {
                sectionB = currentNode.begIndex;
                sectionE = currentNode.endIndex;
                tmp = false;
            }
            ///тут нужно у всех детей переменовать данную переменную во всех детях (шаг наверх уже сделан)
            foreach (IParseNodeType node in currentNode.listOfChildren)
            {

                if ((node.name == "LexLib.LexemNameSpace.VarNameType") && (node.usedVariables[0] == varName) &&
                    (node.begIndex >= sectionB) && (node.endIndex <= sectionE))
                {
                    node.usedVariables[0] = newVar;
                    refPositions.Add(node.begIndex);
                }
                if ((node.usedVariables.Contains(varName)) && (!definitionInChild(varName, node)))
                {
                    Refactor(node);
                }
            }
            return currentNode;
        }

        /// <summary>
        /// из списка детей выдает номер того, у кого начальный и конечный индекс окружают num 
        /// </summary>
        /// <param name="node">узел, среди сыновей котрого нужно выбрать оптимальный</param>
        /// <param name="num">позиция, до котрой нужно спуститься</param>
        /// <returns></returns>
        private int getNumOfOptimalChild(IParseNodeType node)
        {
            int optimal = 0;
            //int i=0;
            for (int i = node.listOfChildren.Count - 1; i >= 0; i--)
            {
                if (node.listOfChildren[i].begIndex <= varNum && node.listOfChildren[i].endIndex > varNum)
                {
                    optimal = i;
                }
                //i++;
            }
            return optimal;

        }

        private bool isTypeDefIn(IParseNodeType node)
        {
            bool res = false;
            foreach (IParseNodeType child in node.listOfChildren)
            {
                try
                {
                    if (child.name.Equals("LexLib.LexemNameSpace.TypeDefinition") && (child.usedVariables[0].Equals(varName)))
                    { res = true; }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception in seacrhing for variable definition");
                    Console.WriteLine(e.StackTrace);
                }
            }
            return res;
        }

        /// <summary>
        /// Вернет true, если в этом узле или его сыновьях объявляется переменная varName
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        private bool definitionInChild(string varName, IParseNodeType tree)
        {
            bool ifDefinition = false;
            foreach(IParseNodeType node in tree.listOfChildren)
            {
                if (node.name.Equals("LexLib.LexemNameSpace.TypeDefinition"))
                {
                    try
                    {
                        if ((node.listOfChildren[0].name.Equals("LexLib.LexemNameSpace.VarNameType")) &&
                            (node.listOfChildren[0].usedVariables[0].Equals(varName)))
                        {
                            return true;
                        }
                        else if ((node.listOfChildren[0].name.Equals("LexLib.LexemNameSpace.Modificator")) &&
                            (node.listOfChildren[0].listOfChildren[0].name.Equals("LexLib.LexemNameSpace.VarNameType")) &&
                            (node.listOfChildren[0].listOfChildren[0].usedVariables[0].Equals(varName)))
                        {
                            return true;
                        }
                        else
                        {
                            ifDefinition = ifDefinition | definitionInChild(varName,node);
                        }
                    }
                    catch (Exception e)
                    {
                        ifDefinition = false;
                    }
                    
                }
            }
            return ifDefinition;
        }
        int t;
        private void tmp1(){
        int myt;
        myt = 4;
        }
    }
}
